﻿using System;
using System.ComponentModel.Composition;
using Contracts;
using System.Reflection;
using Windows7.Location;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.ComponentModel;
using System.Threading;
using FlickrNet;
using System.Windows.Forms;

namespace MefWallpaperAddin
{
    [WpfServiceMetadata("GeoWallpaperAddin")]
    public class GeoWallpaperAddinImpl : IWpfService, INotifyPropertyChanged
    {
        string _status;
        public event EventHandler StatusUpdated;

        GeoWallpaperUserControl _optionsUI = new GeoWallpaperUserControl();
        LocationProvider loc = null;
        System.Threading.Timer updateTimer;
        Random rnd = new Random();

        public GeoWallpaperAddinImpl()
        {
            Console.WriteLine("GeoWallpaper starting up!");
            updateTimer = new System.Threading.Timer(new TimerCallback(UpdateWallpaper), null, Timeout.Infinite, Timeout.Infinite);

            FlickrTags = Properties.Settings.Default.FlickrTags;
            AutoUpdateInterval = Properties.Settings.Default.AutoUpdate;
            AutoUpdateEnabled = Properties.Settings.Default.AutoUpdateEnabled;

            _optionsUI.DataContext = this;
        }

        #region "Service properties"

        public System.Windows.Controls.UserControl OptionsUserControl
        {
            get { return _optionsUI; }
        }

        public string Name
        {
            get { return "GeoWallpaper"; }
        }

        public Uri AddinIconUri
        {
            get {
                string u = CreateResourceUri("Globe.png");

                return new Uri(u);
            }
        }

        private string CreateResourceUri(string name)
        {
            Assembly a = Assembly.GetExecutingAssembly();
            return string.Format("pack://application:,,,/{0};component/Resources/{1}", a.GetName().Name, name);
        }

        public string Description
        {
            get { return "Updates your wallpaper based on current location (requires Windows 7+)"; }
        }

        public string Author
        {
            get { return "Arian T. Kulp"; }
        }

        public Version Version
        {
            get { return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; }
        }

        public Uri AuthorUri
        {
            get { return new Uri("http://www.ariankulp.com/"); }
        }

        public Uri UpdateUri
        {
            get { return new Uri("http://www.ariankulp.com/downloads/"); }
        }

        public string Status
        {
            get { return _status; }
            set
            {
                if (value != _status)
                {
                    _status = value;

                    if (StatusUpdated != null)
                        StatusUpdated(this, null);
                }
            }
        }

        #endregion

        public void Initialize()
        {
            _optionsUI.DataContext = this;
        }

        public void Start()
        {

            if (!EnvironmentSupport.MeetsOrExceeds(PlatformVersion.WINDOWS_7))
            {
                // Fatal error
                throw new UtilityRuntimeException("The GeoWallpaper addin requires Windows 7 or higher to operate");
            }
            else if (string.IsNullOrEmpty(Properties.Settings.Default.FlickrKey) ||
                string.IsNullOrEmpty(Properties.Settings.Default.FlickrSecret))
            {
                throw new UtilityRuntimeException("This application requires a Flickr API key.");
            }
            else
            {
                OptionsUserControl.Dispatcher.Invoke((Action)(() =>
                {
                    F = new Flickr(Properties.Settings.Default.FlickrKey, Properties.Settings.Default.FlickrSecret);
                    if (!string.IsNullOrEmpty(Properties.Settings.Default.AuthToken))
                        F.AuthToken = Properties.Settings.Default.AuthToken;

                    loc = new LatLongLocationProvider(10000);
                    loc.LocationChanged += new LocationChangedEventHandler(loc_LocationChanged);
                    lastLocation = (LatLongLocationReport)(loc.GetReport());

                    FindNearbyImages();
                    UpdateInterval();
                    Status = "GeoWallpaper is started!";
                }));
            }
        }

        void  loc_LocationChanged(LocationProvider locationProvider, LocationReport newLocation)
        {
            Status = "Got a location";
            FindNearbyImages(newLocation as LatLongLocationReport);
            UpdateWallpaper();
        }

        public void UpdateInterval()
        {
            Properties.Settings.Default.Save();

            if (AutoUpdateEnabled)
                updateTimer.Change(0, AutoUpdateInterval * 1000);
            else
                updateTimer.Change(Timeout.Infinite, Timeout.Infinite);
        }

        private string _flickrTags;
        public string FlickrTags
        {
            get{ return _flickrTags;}
            set
            {
                _flickrTags = value;

                NotifyPropertyChanged("FlickrTags");
            }
        }

        private Photo _lastPhoto;
        public Photo LastPhoto
        {
            get { return _lastPhoto; }
            set
            {
                _lastPhoto = value;

                NotifyPropertyChanged("LastPhoto");
            }
        }

        // The number of seconds between refreshes (if enabled)
        private int _autoUpdateInterval;
        public int AutoUpdateInterval
        {
            get { return _autoUpdateInterval; }
            set
            {
                _autoUpdateInterval = value;
                NotifyPropertyChanged("AutoUpdateInterval");
            }
        }

        private bool _autoUpdateEnabled;
        public bool AutoUpdateEnabled
        {
            get { return _autoUpdateEnabled; }
            set
            {
                _autoUpdateEnabled = value;

                NotifyPropertyChanged("AutoUpdateEnabled");
            }
        }

        Flickr F;
        Photo[] photos = null;
        int photoIdx = 0;
        LatLongLocationReport lastLocation;
        string tempFrob;

        public void BeginUserAuth()
        {
            // Get Frob    
            tempFrob = F.AuthGetFrob();
            // Calculate the URL at Flickr to redirect the user to
            string flickrUrl = F.AuthCalcUrl(tempFrob, AuthLevel.Read);
            // The following line will load the URL in the users default browser.
            System.Diagnostics.Process.Start(flickrUrl);
        }

        public void CompleteUserAuth()
        {
            try
            {
                // use the temporary Frob to get the authentication
                Auth auth = F.AuthGetToken(tempFrob);

                // Store this Token for later usage, or set your Flickr instance to use it.
                F.AuthToken = auth.Token;
                Properties.Settings.Default.AuthToken = F.AuthToken;
                Properties.Settings.Default.Save();

                Status = "Flickr authorization successful";
            }
            catch (FlickrException ex)
            {
                // If user did not authenticate your application then a FlickrException will be thrown.
                MessageBox.Show("Authentication did not succeed (" + ex.Message + ").  Please try again if you wish.");
            }
        }

        public void FindNearbyImages()
        {
            if (lastLocation != null)
            {
                OptionsUserControl.Dispatcher.Invoke((Action)(() =>
                {
                    FindNearbyImages(lastLocation);
                }));
            }
        }


        // TODO: Do nothing if location change is not significant enough
        public void FindNearbyImages(LatLongLocationReport location)
        {
            try
            {
                PhotoSearchOptions searchOptions = new PhotoSearchOptions();
                searchOptions.Tags = FlickrTags;
                searchOptions.TagMode = TagMode.AnyTag;
                searchOptions.Latitude = (float)location.Latitude;
                searchOptions.Longitude = (float)location.Longitude;
                searchOptions.Extras = PhotoSearchExtras.OwnerName;
                
                photos = F.PhotosSearch(searchOptions).PhotoCollection;
                
                Properties.Settings.Default.FlickrTags = FlickrTags;
                Properties.Settings.Default.Save();
                photoIdx = rnd.Next(0, photos.Length - 1);
                lastLocation = location;
            }
            catch (Exception x)
            {
                Status = "Flickr exception: " + x.Message;
            }
        }

        public void UpdateWallpaper(object unused)
        {
            this.OptionsUserControl.Dispatcher.Invoke((Action)UpdateWallpaper);
        }

        public void UpdateWallpaper()
        {
            updateTimer.Change(Timeout.Infinite, Timeout.Infinite);

            try
            {
                if (photos != null && photos.Length > 0)
                {
                    Photo photo = photos[photoIdx];
                    if (++photoIdx == photos.Length) photoIdx = 0;

                    string fn = Path.Combine(Path.GetTempPath(), photo.PhotoId + "_" + photo.Secret + ".jpg");

                    // If we've already cached it don't download it again
                    if (!File.Exists(fn))
                    {
                        Sizes s = F.PhotosGetSizes(photo.PhotoId);

                        // Use the highest quality available
                        string url = s.SizeCollection[s.SizeCollection.Length - 1].Source;

                        Stream ds = F.DownloadPicture(url);
                        Stream fs = new FileStream(fn, FileMode.CreateNew);
                        ds.CopyTo(fs);
                        fs.Close();
                        ds.Close();
                    }
                    
                    Status = "Now showing '" + photo.Title + "' by " + photo.OwnerName;

                    LastPhoto = photo;
                    
                    Wallpaper.SetWallpaper(fn, WallpaperStyle.Fit);

                }
                else Status = "No photos found for this location";
            }
            catch (Exception x)
            {
                Status = "Error updating wallpaper: " + x.Message;
            }
            finally
            {
                if (AutoUpdateEnabled)
                    updateTimer.Change(AutoUpdateInterval * 1000, AutoUpdateInterval * 1000);
            }
        }

        public void Stop()
        {
            updateTimer.Change(Timeout.Infinite, Timeout.Infinite);

            Status = "GeoWallpaper Stopped";
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string prop)
        {
            if (PropertyChanged != null)
            { PropertyChanged(this, new PropertyChangedEventArgs(prop));
            }
        }
    }
}

